# TODO: Add comment
# 
# Author: rogb
###############################################################################
DateYMD <- function(Year,Month,Day){
	# Year = 2009
	# Month = 11
	# Day = 30
	.Internal(POSIXlt2Date(strptime(paste(Year,Month,Day,sep="-"),"%Y-%m-%d", tz = "GMT")))
}
day <- function(Date){
	# Date <- Sys.Date()
	as.numeric(format(Date,"%d"))
}
month <- function(Date){
	# Date <- Sys.Date()
	as.numeric(format(Date,"%m"))
}
year <- function(Date){
	# Date <- Sys.Date()
	as.numeric(format(Date,"%Y"))
}
"day<-" <- function(Date,value){
	Y <- year(Date)
	M <- month(Date)
	D <- value
	DateYMD(Y,M,D)
}
"month<-" <- function(Date,value){
	Y <- year(Date)
	M <- value
	D <- day(Date)
	DateYMD(Y,M,D)
}
"year<-" <- function(Date,value){
	Y <- value
	M <- month(Date)
	D <- day(Date)
	DateYMD(Y,M,D)
}
getWeekday <- function(Date){
	# Date <- Sys.Date()
	# 0 -> Sunday
	# 1 -> Monday
	# 2 -> Tuesday
	# 3 -> Wednesday
	# 4 -> Thursday
	# 5 -> Friday
	# 6 -> Saturday
	as.numeric(format(Date,"%w"))
}
getNthWeekday <- function(Date){
	Day <- day(Date)
	(Day-1)%/%7+1
}
isNthWeekday <- function(Date,Weekday,NthWeekday){
	# Date <- Sys.Date()
	# Weekday <- 4
	# NthWeekday <- 2
	getWeekday(Date)==Weekday & getNthWeekday(Date)==NthWeekday
}
getDateOfNthWeekday <- function(Year,Month,Weekday,NthWeekday){
	# Year <- 2009
	# Month <- 12
	# Weekday <- 5 # Friday
	# NthWeekday <- 3 # Third Friday in Month (EQ Futures)
	# Year <- expand.grid(1:12,1900:2109)[,2]
	# Month <- expand.grid(1:12,1900:2109)[,1] 
	FirstDayInMonth <- DateYMD(Year,Month,1)
	DayShift <- Weekday - getWeekday(FirstDayInMonth) 
	correction <- cbind(DayShift<0,DayShift>=0)%*%c(0,1)
	Day <- (NthWeekday-correction) * 7 + DayShift + 1
	Date <- DateYMD(Year,Month,Day)
	Date
}
isEndOfMonth <- function(Date){
	# Date <- Sys.Date()
	day(Date+1)==1 
}
getEndOfMonth <- function(Year,Month){
	# Date <- Sys.Date() + 0:364
	# Year <- year(Date)
	# Month <- month(Date)
	Y <- Year
	M <- Month
	Y[Month==12] <- Year[Month==12]+1
	M[Month==12] <- 1
	M[Month!=12] <- Month[Month!=12] + 1
	DateYMD(Y,M,1)-1
}
getLastDayOfMonth <- function(Year,Month){
	# Year <- 2009
	# Month <- 12
	day(getEndOfMonth(Year,Month))
}
isLastFebruary <- function(Date){
	# Date <- Sys.Date()
	day(Date+1)==1 & month(Date+1)==3
}
isLastWeekday <- function(Date){
	# Date <- Sys.Date()
	# Date <- Sys.Date()+0:30000
	Result <- day(Date)>21
	DMax <- getLastDayOfMonth(year(Date[Result]),month(Date[Result]))
	Result[Result] <- (DMax - day(Date[Result])) < 7
	Result
}
getLastWeekday <- function(Year,Month,Weekday){
	# Year <- 2009
	# Month <- 12
	# Weekday <- 2 # Friday
	# Year <- expand.grid(1:12,1900:2109)[,2]
	# Month <- expand.grid(1:12,1900:2109)[,1] 
	LastDayInMonth <- getEndOfMonth(Year,Month)
	DayShift <- Weekday - getWeekday(LastDayInMonth) 
	correction <- cbind(DayShift<=0,DayShift>0)%*%c(0,1)
	Day <- day(LastDayInMonth)-correction*7 + DayShift
	Date <- DateYMD(Year,Month,Day)
	Date
}

yearCount <- function(StartDate,EndDate){
	# InterestStartDate <- DateYMD(2009,2,28)
	# InterestPayDate <- Sys.Date()+(1000:1010)*1:11
	# InterestPayDate <- DateYMD(2012,2,29)
	# yearCount(InterestStartDate,InterestPayDate)
	# yearCount(InterestStartDate,InterestPayDate,returnClass="numeric")
	# InterestPayDate <- Sys.Date()+1000:1010
	nStartDate <- length(StartDate)
	nEndDate <- length(EndDate)
	if(nStartDate==1 & nEndDate!=1){
		StartDate <- rep(StartDate,nEndDate)
	}
	if(nStartDate!=1 & nEndDate==1){
		EndDate <- rep(EndDate,nStartDate)
	}
	nStartDate <- length(StartDate)
	nEndDate <- length(EndDate)
	if(nStartDate!=nEndDate){
		stop("StartDate and EndDate have incorrect length!")
	}
	Y1 <- year(StartDate)
	M1 <- month(StartDate)
	D1 <- day(StartDate)
	Y2 <- year(EndDate)
	idxFebruary <- isLastFebruary(StartDate)
	D1[idxFebruary] <- getLastDayOfMonth(Y2[idxFebruary],2)
	nYear <- Y2 - Y1
	nYear[DateYMD(Y2,M1,D1)>EndDate & StartDate<EndDate] <- nYear[DateYMD(Y2,M1,D1)>EndDate & StartDate<EndDate] - 1
	nYear[DateYMD(Y2,M1,D1)<EndDate & StartDate>EndDate] <- nYear[DateYMD(Y2,M1,D1)<EndDate & StartDate>EndDate] + 1
	nYear
}

yearFraction <- function(StartDate,EndDate){
	# InterestStartDate <- Sys.Date()
	# InterestPayDate <- Sys.Date()+(1000:1010)*1:11
	# InterestPayDate <- Sys.Date()+1000
	# yearCount(InterestStartDate,InterestPayDate)
	# yearCount(InterestStartDate,InterestPayDate,returnClass="numeric")
	# InterestPayDate <- Sys.Date()+1000:1010
	nStartDate <- length(StartDate)
	nEndDate <- length(EndDate)
	if(nStartDate==1 & nEndDate!=1){
		StartDate <- rep(StartDate,nEndDate)
	}
	if(nStartDate!=1 & nEndDate==1){
		EndDate <- rep(EndDate,nStartDate)
	}
	nStartDate <- length(StartDate)
	nEndDate <- length(EndDate)
	if(nStartDate!=nEndDate){
		stop("StartDate and EndDate have incorrect length!")
	}
	
	Y1 <- year(StartDate)
	M1 <- month(StartDate)
	D1 <- day(StartDate)
	
	nYear <- yearCount(StartDate,EndDate)
	Y3 <- Y1 + nYear
	M3 <- M1
	D3 <- D1
	
	Y4 <- Y3
	Y4[StartDate<=EndDate] <- Y4[StartDate<=EndDate] + 1
	Y4[StartDate>EndDate] <- Y4[StartDate>EndDate] - 1
	M4 <- M1
	D4 <- D1
	
	idxFebruary <- isLastFebruary(StartDate)
	
	D3[idxFebruary] <- getLastDayOfMonth(Y3[idxFebruary],2)
	FractionStartDate <- DateYMD(Y3,M3,D3)
	
	D4[idxFebruary] <- getLastDayOfMonth(Y4[idxFebruary],2)
	FractionEndDate <- DateYMD(Y4,M4,D4)
	
	Fraction <- as.numeric(EndDate - FractionStartDate)/as.numeric(FractionEndDate - FractionStartDate)	
	Fraction[EndDate<FractionStartDate] <- Fraction[EndDate<FractionStartDate] * -1
	nYear + Fraction
}

monthlyDateSequence <- function(StartDate,EndDate,Frequency,Direction=1,overlapping=TRUE,keepEndOfMonth=TRUE,keepNthWeekday=FALSE,keepLastWeekday=FALSE){
	# StartDate <- DateYMD(2009,10,30)
	# EndDate <- DateYMD(2020,11,30)
	# Direction <- 1
	# Frequency <- 3
	# overlapping <- TRUE
	# keepEndOfMonth <- TRUE
	# keepNthWeekday=FALSE
	# keepLastWeekday=FALSE
	# monthlyDateSequence(DateYMD(2009,10,30),DateYMD(2020,11,30),3,1,overlapping=FALSE)
	if(Direction==1){
		FromDate <- StartDate
		ToDate <- EndDate
	}
	if(Direction==-1){
		FromDate <- EndDate
		ToDate <- StartDate
	}
	
	FY <- year(FromDate)
	FM <- month(FromDate)
	FD <- day(FromDate)
	TY <- year(ToDate)
	TM <- month(ToDate)
	TD <- day(ToDate)
	
	MonthShift <- Direction*(12 / Frequency)
	nMonth <- yearFraction(FromDate,ToDate)*12
	nStep <- nMonth / MonthShift
	MaxStep <- c(ceiling(nStep),floor(nStep-0.001))[c(overlapping,!overlapping)] * MonthShift
	Month <- (FM + seq(0,MaxStep,MonthShift))%%12
	DeltaYear <- (FM + seq(0,MaxStep,MonthShift))%/%12
	DeltaYear[Month==0] <- DeltaYear[Month==0] -1
	Month[Month==0] <- 12
	Year <- FY + DeltaYear
	
	## manage all different from date problems here!
	## standard case where from day is non special
	Day <- rep(FD,length(Year))
	
	####################################################
	
	if(isEndOfMonth(FromDate) & keepEndOfMonth==TRUE){
		Day <- getLastDayOfMonth(Year,Month) 	
	}
	if(isEndOfMonth(FromDate) & keepEndOfMonth==FALSE & (FD==29 | FD==30)){
		Day[Month==2] <- getLastDayOfMonth(Year[Month==2],Month[Month==2]) 	
	}
	if(isEndOfMonth(FromDate) & keepEndOfMonth==FALSE & FD==31){
		Day <- getLastDayOfMonth(Year,Month) 	
	}
	if(!isEndOfMonth(FromDate) & (FD==29 | FD==30)){
		EndMonthDay <- getLastDayOfMonth(Year,Month)
		Day[Day>EndMonthDay] <- EndMonthDay[Day>EndMonthDay]
	}
	
	
	
	
	if(keepLastWeekday==TRUE){
		if(!isLastWeekday(FromDate)){
			stop(FD,"th is not last weekday in month",sep="")
		}
		Weekday <- getWeekday(FromDate)
		Day <- day(getLastWeekday(Year,Month,Weekday))
	}
	
	if(keepNthWeekday==TRUE){
		Weekday <- getWeekday(FromDate)
		NthWeekday <- getNthWeekday(FromDate)
		Day <- day(getDateOfNthWeekday(Year,Month,Weekday,NthWeekday))
	}
	
	####################################################
	
	sort(DateYMD(Year,Month,Day))
	
}
# dateSequence(Sys.Date(),DateYMD(2020,09,15),2,-1,overlapping=T,keepEndOfMonth=F,keepLastWeekday=T)



